home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / networking / ottraceroutesample / ottraceroutesample.c next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  8.5 KB  |  310 lines

  1. /*
  2.     File:                OTTraceRouteSample.c
  3.  
  4.     Contains:        A trivial traceroute implementation.
  5.  
  6.     Written by: Quinn "The Eskimo!"    
  7.  
  8.     Copyright:    Copyright © 1996-1999 by Apple Computer, Inc., All Rights Reserved.
  9.  
  10.                             You may incorporate this Apple sample source code into your program(s) without
  11.                             restriction. This Apple sample source code has been provided "AS IS" and the
  12.                             responsibility for its operation is yours. You are not permitted to redistribute
  13.                             this Apple sample source code as "Apple sample source code" after having made
  14.                             changes. If you're going to re-distribute the source, we require that you make
  15.                             it clear in the source that the code was descended from Apple sample source
  16.                             code, but that you've made changes.
  17.  
  18.     Change History (most recent first):
  19.                             7/23/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  20.                 
  21.  
  22. */
  23. #include <stdio.h>
  24. #include <OpenTransport.h>
  25. #include <OpenTptInternet.h>
  26. #include <Events.h>
  27. /////////////////////////////////////////////////////////////////////
  28.  
  29. static OSStatus CreateAndConfigUDP(EndpointRef *ep)
  30. {
  31.     OSStatus err;
  32.     
  33.     *ep = OTOpenEndpoint(OTCreateConfiguration(kUDPName), 0, nil, &err);
  34.     
  35.     if (err == noErr) {
  36.     err = OTBind(*ep, nil, nil);
  37.     
  38.         // no others options to negotiate at this stage
  39.     }
  40.     
  41.     return (err);
  42. }
  43.  
  44. /////////////////////////////////////////////////////////////////////
  45.  
  46. static OSStatus CreateAndConfigICMP(EndpointRef *ep)
  47. {
  48.     OSStatus err;
  49.     
  50.     *ep = OTOpenEndpoint(OTCreateConfiguration(kRawIPName), 0, nil, &err);
  51.     
  52.     if (err == noErr) {
  53.     err = OTBind(*ep, nil, nil);
  54.     
  55.         // no others options to negotiate at this stage
  56.         
  57.         // You might think we need to negotiate the XTI_GENERIC/XTI_PROTOTYPE
  58.         // option to request ICMP packets (ie protocol 2).  This is not
  59.         //  necessary because rawip endpoints default to that protocol.
  60.     }
  61.     
  62.     return (err);
  63. }
  64.  
  65. /////////////////////////////////////////////////////////////////////
  66.  
  67. static OSStatus DoNegotiateIP_TTLOption(EndpointRef ep, long ttl)
  68. {
  69.     // According to the XTI spec, IP_TTL is an INET_IP level option that
  70.     //  determines the TTL of an IP packet.  The value of this option is
  71.     //  a UInt8.  This routine simply negotiates that option on the ep
  72.     //  endpoint.
  73.     OSStatus    err;
  74.     TOption*    opt;                                                // points to buf, makes it easier to access
  75.     TOptMgmt    req;
  76.     TOptMgmt    ret;
  77.     UInt8            buf[kOTFourByteOptionSize];    // define buffer for options, although we only
  78.                                                                                 // use a "1 byte option", we define a "4 byte option"
  79.                                                                                 // buffer to hold the returning options
  80.     
  81.     // Point opt to the start of buf.  This allows us to set the items in buf easily.
  82.     opt = (TOption*)buf;
  83.  
  84.     // Setup the fields of the options buffer...
  85.     
  86.     opt->level    = INET_IP;
  87.     opt->name    = IP_TTL;
  88.     opt->len    = kOTOneByteOptionSize;            // Note that kOTOneByteOptionSize != 1, it also
  89.     opt->status = 0;                                            //  includes the size of the option header.
  90.     *(UInt8*)opt->value = ttl;
  91.  
  92.     // Set up the req structure to denote the options we're requesting...
  93.     
  94.     req.opt.buf    = buf;
  95.     req.opt.len    = kOTOneByteOptionSize;
  96.     req.flags    = T_NEGOTIATE;
  97.  
  98.     // Set up the ret structure to hold the options we got...
  99.     
  100.     ret.opt.buf = buf;
  101.     ret.opt.maxlen = kOTFourByteOptionSize;
  102.  
  103.     err = OTOptionManagement(ep, &req, &ret);
  104.     
  105.     // If no error then return the option status value...
  106.     
  107.     if (err == kOTNoError) {
  108.         if (opt->status != T_SUCCESS)
  109.             err = opt->status;
  110.         else
  111.             err = kOTNoError;
  112.     }
  113.                 
  114.     return (err);
  115. }
  116.  
  117. /////////////////////////////////////////////////////////////////////
  118.  
  119. // this is the data we send in our UDP packets...
  120.  
  121. static unsigned char udp_data[8] = {0, 1, 2, 3, 4, 5, 6, 7};
  122.  
  123. /////////////////////////////////////////////////////////////////////
  124.  
  125. static OSStatus SendUDPWithTTL(EndpointRef ep, InetHost dest, long ttl)
  126. {
  127.     OSStatus err;
  128.     InetAddress dest_addr;
  129.     TUnitData udata;
  130.     OTResult look;
  131.     
  132.     err = DoNegotiateIP_TTLOption(ep, ttl);
  133.  
  134.     if (err == noErr) {
  135.  
  136.         OTInitInetAddress(&dest_addr, 33434, dest);        
  137.  
  138.         // 33434 is the default port for unix traceroute.
  139.         //  It was chosen because it's unlikely that anyone will be listening on this
  140.         //  port.  Hence any packets that make it through will generate an ICMP
  141.         //  port unreachable error.
  142.  
  143.         udata.addr.len = sizeof(dest_addr);
  144.         udata.addr.buf = (unsigned char *) &dest_addr;
  145.         
  146.         udata.opt.len = 0;
  147.         udata.opt.buf = nil;
  148.         
  149.         udata.udata.len = sizeof(udp_data);
  150.         udata.udata.buf = &udp_data[0];
  151.  
  152.         // The act of sending is a little more complicated than it should be.
  153.         //  Basically the ICMP errors that come back from all these bogus (short TTL)
  154.         //  packets that I send, end up as datagram errors on the sending endpoint.
  155.         //  If you attempt to send with a T_UDERR sitting on the endpoint, you get
  156.         //  a kOTLookErr.
  157.         //
  158.         // I addresses this by junking the error and looping when I get a T_UDERR.
  159.         
  160.         do {
  161.             err = OTSndUData(ep, &udata);
  162.             if (err == kOTLookErr) {
  163.                 look = OTLook(ep);
  164.                 if (look == T_UDERR) {
  165.                     printf("•Junking T_UDERR.\n");
  166.                     fflush(stdout);
  167.                     (void) OTRcvUDErr(ep, nil);        // clear the error condition without receiving the error info
  168.                     err = 666;                                        // and attempt to send again
  169.                     // Yeah, yeah, I know that using error codes to control program flow is bad
  170.                     //  style.  Hey, I was in hurry!        
  171.                 }
  172.             }
  173.         } while (err == 666);
  174.     }
  175.  
  176.     return (err);
  177. }
  178.  
  179. /////////////////////////////////////////////////////////////////////
  180.  
  181. // we use this buffer to hold incoming ICMP packets
  182.  
  183. static UInt8 icmp_data[5000];
  184.  
  185. /////////////////////////////////////////////////////////////////////
  186.  
  187. static OSStatus WaitAndPrintICMPs(EndpointRef ep, Boolean *done)
  188. {
  189.     TUnitData udata;
  190.     long start_time;
  191.     OSStatus err;
  192.     InetAddress src_addr;
  193.     
  194.     start_time = TickCount();
  195.     
  196.     // Wait for 3 seconds and print out any ICMP packets we get back.
  197.     
  198.     do {
  199.  
  200.         // Set up the received...
  201.         udata.addr.buf = (UInt8*) &src_addr;
  202.         udata.addr.maxlen = sizeof(struct InetAddress);
  203.         udata.opt.buf = nil;
  204.         udata.opt.maxlen = 0;
  205.         udata.udata.buf = icmp_data;
  206.         udata.udata.maxlen = sizeof(icmp_data);
  207.         
  208.         // Look for a packet...
  209.         
  210.         err = OTRcvUData(ep, &udata, nil);
  211.         if (err == noErr) {
  212.             // Print out salient information from the packet...
  213.             printf("•••Got packet!•••\n");
  214.             
  215.             printf("ICMP from = %d.%d.%d.%d\n", icmp_data[12], icmp_data[13], icmp_data[14], icmp_data[15]);
  216.             printf("ICMP type = %d\n", icmp_data[20]);
  217.             printf("ICMP code = %d\n", icmp_data[21]);
  218.             
  219.             // Stop if the traceroute is at an end.  Note that this code assumes that
  220.             //  the ICMP header will start 20 bytes into the packet.  This is correct
  221.             //  for 99% of IP packets, but not correct in general.  If the IP packet
  222.             //  has IP level options, they will be inserted between the 20 byte IP
  223.             //  header and the payload, thereby stuffing up this calculation.  I was
  224.             //  slack and ignored this issue.  You should not!
  225.             
  226.             if (icmp_data[20] == 3 && icmp_data[21] == 3) {
  227.                 // type 3 = destination unreachable
  228.                 // code 3 = port unreachable
  229.                 // These two imply that we're trying to deliver the packet on the destination
  230.                 //  host, and it couldn't be delivered because the port is wrong.  The fact
  231.                 //  that we're hitting the destination host means we can stop the trace.
  232.                 *done = true;
  233.             }
  234.             
  235.             fflush(stdout);
  236.         } else if (err == kOTNoDataErr) {
  237.             err = noErr;
  238.         }
  239.     } while (err == noErr && TickCount() < start_time + 3 * 60 && !*done);
  240.  
  241.     return (err);
  242. }
  243.  
  244. /////////////////////////////////////////////////////////////////////
  245.  
  246. static OSStatus DoTraceRoute(InetHost dest)
  247. {
  248.     OSStatus err;
  249.     EndpointRef udp_ep = nil;
  250.     EndpointRef icmp_ep = nil;
  251.     long ttl;
  252.     Boolean done;
  253.     
  254.     // Create the endpoints and negotiate the options...
  255.     err = CreateAndConfigUDP(&udp_ep);
  256.     if (err == noErr) {
  257.         err = CreateAndConfigICMP(&icmp_ep);
  258.     }
  259.     
  260.     // Do the main traceroute loop...
  261.     
  262.     ttl = 1;
  263.     done = false;
  264.     do {
  265.         printf("\nSending with TTL = %d.\n", ttl);
  266.         err = SendUDPWithTTL(udp_ep, dest, ttl);
  267.         if (err == noErr) {
  268.             err = WaitAndPrintICMPs(icmp_ep, &done);
  269.         }
  270.         if (err == noErr) {
  271.             ttl += 1;
  272.         }
  273.     } while (err == noErr && ttl < 30 && !done);
  274.  
  275.     if (done) {
  276.         printf("Traceroute completed successfully!\n");
  277.     }
  278.  
  279.     // clean up
  280.     if (udp_ep != nil) {
  281.         (void) OTCloseProvider(udp_ep);
  282.     }
  283.     if (icmp_ep != nil) {
  284.         (void) OTCloseProvider(icmp_ep);
  285.     }
  286.     return err;
  287. }
  288.  
  289. /////////////////////////////////////////////////////////////////////
  290.  
  291. void main(void) {
  292.     OSStatus err;
  293.     
  294.     printf("Hello Cruel World!\n");
  295.     
  296.     err = InitOpenTransport();
  297.     if (err == noErr) {
  298.         
  299.         err = DoTraceRoute(0x822B0202);            // apple.com
  300.     
  301.         CloseOpenTransport();
  302.     }
  303.     
  304.     if (err == noErr) {
  305.         printf("Success!\n");
  306.     } else {
  307.         printf("Failure!  Error = %d.\n", err);
  308.     }
  309.     printf("Done.  Press command-Q to Quit.\n");
  310. }